/**
 * @file aiot_tcp_api.c
 * @brief TCP模块实现, 其中包含了向物联网平台认证和上报数据的API接口
 * @date 2019-12-27
 *
 * @copyright Copyright (C) 2015-2018 Alibaba Group Holding Limited
 *
 */

#include "core_tcp.h"

static void _core_tcp_exec_inc(core_tcp_handle_t *tcp_handle)
{
    tcp_handle->sysdep->core_sysdep_mutex_lock(tcp_handle->data_mutex);
    tcp_handle->core_exec_count++;
    tcp_handle->sysdep->core_sysdep_mutex_unlock(tcp_handle->data_mutex);
}

static void _core_tcp_exec_dec(core_tcp_handle_t *tcp_handle)
{
    tcp_handle->sysdep->core_sysdep_mutex_lock(tcp_handle->data_mutex);
    tcp_handle->core_exec_count--;
    tcp_handle->sysdep->core_sysdep_mutex_unlock(tcp_handle->data_mutex);
}

static int32_t _core_tcp_sysdep_return(int32_t sysdep_code, int32_t core_code)
{
    if (sysdep_code >= (STATE_PORT_BASE - 0x00FF) && sysdep_code < (STATE_PORT_BASE)) {
        return sysdep_code;
    } else {
        return core_code;
    }
}

static int32_t _core_tcp_connect(core_tcp_handle_t *tcp_handle)
{
    int32_t res = STATE_SUCCESS;
    char host[128];
    uint16_t port = 0;
    uint32_t port_u32 = 0;
    char *ptr = NULL;
    /* disconnect first if network is already established */
    if (tcp_handle->network_handle != NULL) {
        tcp_handle->sysdep->core_sysdep_network_deinit(&tcp_handle->network_handle);
    }
    if (tcp_handle->host == NULL) {
        return STATE_USER_INPUT_MISSING_HOST;
    }
    if(strlen(tcp_handle->host) > sizeof(host)){
        return STATE_PORT_INPUT_OUT_RANGE;
    }

    /* establish network connection */
    core_sysdep_socket_type_t socket_type = CORE_SYSDEP_SOCKET_TCP_CLIENT;

    memset(host, 0, sizeof(host));
    ptr = strstr(tcp_handle->host, ":");
    if(ptr == NULL) {
        memcpy(host, tcp_handle->host, strlen(tcp_handle->host));
        port = tcp_handle->port;
    } else {
        memcpy(host, tcp_handle->host, ptr - tcp_handle->host);
        core_str2uint(ptr + 1, strlen(ptr + 1), &port_u32);
        port = port_u32;
    }

    tcp_handle->network_handle = tcp_handle->sysdep->core_sysdep_network_init();
    if (tcp_handle->network_handle == NULL) {
        return STATE_SYS_DEPEND_MALLOC_FAILED;
    }

    if ((res = tcp_handle->sysdep->core_sysdep_network_setopt(tcp_handle->network_handle, CORE_SYSDEP_NETWORK_SOCKET_TYPE,
               &socket_type)) < 0 ||
        (res = tcp_handle->sysdep->core_sysdep_network_setopt(tcp_handle->network_handle, CORE_SYSDEP_NETWORK_HOST,
                host)) < 0 ||
        (res = tcp_handle->sysdep->core_sysdep_network_setopt(tcp_handle->network_handle, CORE_SYSDEP_NETWORK_PORT,
                &port)) < 0 ||
        (res = tcp_handle->sysdep->core_sysdep_network_setopt(tcp_handle->network_handle,
                CORE_SYSDEP_NETWORK_CONNECT_TIMEOUT_MS,
                &tcp_handle->connect_timeout_ms)) < 0) {
        tcp_handle->sysdep->core_sysdep_network_deinit(&tcp_handle->network_handle);
        return _core_tcp_sysdep_return(res, STATE_SYS_DEPEND_NWK_INVALID_OPTION);
    }

    if (tcp_handle->cred != NULL) {
        res = tcp_handle->sysdep->core_sysdep_network_setopt(tcp_handle->network_handle, CORE_SYSDEP_NETWORK_CRED,
                tcp_handle->cred);
        if (res < STATE_SUCCESS) {
            tcp_handle->sysdep->core_sysdep_network_deinit(&tcp_handle->network_handle);
            return _core_tcp_sysdep_return(res, STATE_SYS_DEPEND_NWK_INVALID_CRED);
        }
    }

    res = tcp_handle->sysdep->core_sysdep_network_establish(tcp_handle->network_handle);
    if (res < STATE_SUCCESS) {
        tcp_handle->sysdep->core_sysdep_network_deinit(&tcp_handle->network_handle);
        return _core_tcp_sysdep_return(res, STATE_SYS_DEPEND_NWK_EST_FAILED);
    }

    return STATE_SUCCESS;
}

static int32_t _core_tcp_send(core_tcp_handle_t *tcp_handle, uint8_t *buffer, uint32_t len, uint32_t timeout_ms)
{
    int32_t res = STATE_SUCCESS;

    if (tcp_handle->network_handle != NULL) {
        res = tcp_handle->sysdep->core_sysdep_network_send(tcp_handle->network_handle, buffer, len, timeout_ms, NULL);
        if (res < STATE_SUCCESS) {
            tcp_handle->sysdep->core_sysdep_network_deinit(&tcp_handle->network_handle);
            core_log(tcp_handle->sysdep, STATE_TCP_LOG_DISCONNECT, "TCP network error when sending data, disconnect\r\n");
            res = _core_tcp_sysdep_return(res, STATE_SYS_DEPEND_NWK_SEND_ERR);
        } else if (res != len) {
            res = STATE_SYS_DEPEND_NWK_WRITE_LESSDATA;
        }
    } else {
        res = STATE_SYS_DEPEND_NWK_CLOSED;
    }

    return res;
}

static int32_t _core_tcp_send_body(core_tcp_handle_t *tcp_handle, uint8_t *content, uint32_t len)
{
    int32_t res = STATE_SUCCESS;

    core_log_hexdump(STATE_TCP_LOG_SEND_CONTENT, '>', content, len);

    tcp_handle->sysdep->core_sysdep_mutex_lock(tcp_handle->send_mutex);
    res = _core_tcp_send(tcp_handle, content, len, tcp_handle->send_timeout_ms);
    tcp_handle->sysdep->core_sysdep_mutex_unlock(tcp_handle->send_mutex);

    return res;
}

void *core_tcp_init(void)
{
    core_tcp_handle_t *tcp_handle = NULL;
    aiot_sysdep_portfile_t *sysdep = NULL;

    sysdep = aiot_sysdep_get_portfile();
    if (sysdep == NULL) {
        return NULL;
    }

    tcp_handle = sysdep->core_sysdep_malloc(sizeof(core_tcp_handle_t), CORE_TCP_MODULE_NAME);
    if (tcp_handle == NULL) {
        return NULL;
    }
    memset(tcp_handle, 0, sizeof(core_tcp_handle_t));

    tcp_handle->sysdep = sysdep;
    tcp_handle->connect_timeout_ms = CORE_TCP_DEFAULT_CONNECT_TIMEOUT_MS;
    tcp_handle->send_timeout_ms = CORE_TCP_DEFAULT_SEND_TIMEOUT_MS;
    tcp_handle->recv_timeout_ms = CORE_TCP_DEFAULT_RECV_TIMEOUT_MS;
    tcp_handle->header_line_max_len = CORE_TCP_DEFAULT_HEADER_LINE_MAX_LEN;
    tcp_handle->body_buffer_max_len = CORE_TCP_DEFAULT_BODY_MAX_LEN;
    tcp_handle->deinit_timeout_ms = CORE_TCP_DEFAULT_DEINIT_TIMEOUT_MS;

    tcp_handle->data_mutex = tcp_handle->sysdep->core_sysdep_mutex_init();
    tcp_handle->send_mutex = tcp_handle->sysdep->core_sysdep_mutex_init();
    tcp_handle->recv_mutex = tcp_handle->sysdep->core_sysdep_mutex_init();

    tcp_handle->core_exec_enabled = 1;

    return tcp_handle;
}

int32_t core_tcp_setopt(void *handle, core_tcp_option_t option, void *data)
{
    int32_t res = STATE_SUCCESS;
    core_tcp_handle_t *tcp_handle = (core_tcp_handle_t *)handle;

    if (tcp_handle == NULL || data == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    if (option >= CORE_TCPOPT_MAX) {
        return STATE_USER_INPUT_OUT_RANGE;
    }

    if (tcp_handle->core_exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    _core_tcp_exec_inc(tcp_handle);

    tcp_handle->sysdep->core_sysdep_mutex_lock(tcp_handle->data_mutex);
    switch (option) {
        case CORE_TCPOPT_HOST: {
            res = core_strdup(tcp_handle->sysdep, &tcp_handle->host, (char *)data, CORE_TCP_MODULE_NAME);
        }
        break;
        case CORE_TCPOPT_PORT: {
            tcp_handle->port = *(uint16_t *)data;
        }
        break;
        case CORE_TCPOPT_NETWORK_CRED: {
            if (tcp_handle->cred != NULL) {
                tcp_handle->sysdep->core_sysdep_free(tcp_handle->cred);
                tcp_handle->cred = NULL;
            }
            tcp_handle->cred = tcp_handle->sysdep->core_sysdep_malloc(sizeof(aiot_sysdep_network_cred_t), CORE_TCP_MODULE_NAME);
            if (tcp_handle->cred != NULL) {
                memset(tcp_handle->cred, 0, sizeof(aiot_sysdep_network_cred_t));
                memcpy(tcp_handle->cred, data, sizeof(aiot_sysdep_network_cred_t));
            } else {
                res = STATE_SYS_DEPEND_MALLOC_FAILED;
            }

        }
        break;
        case CORE_TCPOPT_CONNECT_TIMEOUT_MS: {
            tcp_handle->connect_timeout_ms = *(uint32_t *)data;
        }
        break;
        case CORE_TCPOPT_SEND_TIMEOUT_MS: {
            tcp_handle->send_timeout_ms = *(uint32_t *)data;
        }
        break;
        case CORE_TCPOPT_RECV_TIMEOUT_MS: {
            tcp_handle->recv_timeout_ms = *(uint32_t *)data;
        }
        break;
        case CORE_TCPOPT_DEINIT_TIMEOUT_MS: {
            tcp_handle->deinit_timeout_ms = *(uint32_t *)data;
        }
        break;
        case CORE_TCPOPT_HEADER_LINE_MAX_LEN: {
            tcp_handle->header_line_max_len = *(uint32_t *)data;
        }
        break;
        case CORE_TCPOPT_BODY_BUFFER_MAX_LEN: {
            tcp_handle->body_buffer_max_len = *(uint32_t *)data;
        }
        break;
        case CORE_TCPOPT_EVENT_HANDLER: {
            tcp_handle->event_handler = (aiot_tcp_event_handler_t)data;
        }
        break;
        case CORE_TCPOPT_USERDATA: {
            tcp_handle->core_userdata = data;
        }
        break;
        case CORE_TCPOPT_RECV_HANDLER: {
            tcp_handle->core_recv_handler = (aiot_tcp_recv_handler_t)data;
        }
        break;
        default: {
            res = STATE_USER_INPUT_UNKNOWN_OPTION;
        }
        break;
    }
    tcp_handle->sysdep->core_sysdep_mutex_unlock(tcp_handle->data_mutex);

    _core_tcp_exec_dec(tcp_handle);

    return res;
}

int32_t core_tcp_connect(void *handle)
{
    int32_t res = STATE_SUCCESS;
    core_tcp_handle_t *tcp_handle = (core_tcp_handle_t *)handle;

    if (tcp_handle == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    if (tcp_handle->core_exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    _core_tcp_exec_inc(tcp_handle);

    /* connect to host */
    tcp_handle->sysdep->core_sysdep_mutex_lock(tcp_handle->send_mutex);
    tcp_handle->sysdep->core_sysdep_mutex_lock(tcp_handle->recv_mutex);
    res = _core_tcp_connect(tcp_handle);
    tcp_handle->sysdep->core_sysdep_mutex_unlock(tcp_handle->recv_mutex);
    tcp_handle->sysdep->core_sysdep_mutex_unlock(tcp_handle->send_mutex);

    _core_tcp_exec_dec(tcp_handle);

    return res;
}

int32_t core_tcp_send(void *handle, uint8_t *content, uint32_t len)
{
    int32_t res = STATE_SUCCESS;
    // char content_lenstr[11] = {0};
    core_tcp_handle_t *tcp_handle = (core_tcp_handle_t *)handle;

    if (content == NULL || len <= 0) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    if (tcp_handle->host == NULL) {
        return STATE_USER_INPUT_MISSING_HOST;
    }

    if (tcp_handle->network_handle == NULL) {
        return STATE_SYS_DEPEND_NWK_CLOSED;
    }

    if (tcp_handle->core_exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    memset(&tcp_handle->session, 0, sizeof(core_tcp_session_t));

    _core_tcp_exec_inc(tcp_handle);

    /* send tcp content */
    if (content != NULL && len > 0) {
        res = _core_tcp_send_body(tcp_handle, content, len);
        if (res < STATE_SUCCESS) {
            _core_tcp_exec_dec(tcp_handle);
            return res;
        }
    }

    _core_tcp_exec_dec(tcp_handle);

    return res;
}

static int32_t _core_tcp_recv(core_tcp_handle_t *tcp_handle, uint8_t *buffer, uint32_t len, uint32_t timeout_ms)
{
    int32_t res = STATE_SUCCESS;

    if (tcp_handle->network_handle != NULL) {
        res = tcp_handle->sysdep->core_sysdep_network_recv(tcp_handle->network_handle, buffer, len, timeout_ms, NULL);
        if (res < STATE_SUCCESS) {
            tcp_handle->sysdep->core_sysdep_network_deinit(&tcp_handle->network_handle);
            core_log(tcp_handle->sysdep, STATE_TCP_LOG_DISCONNECT, "TCP network error when receving data, disconnect\r\n");
            res = _core_tcp_sysdep_return(res, STATE_SYS_DEPEND_NWK_RECV_ERR);
        }
    } else {
        res = STATE_SYS_DEPEND_NWK_CLOSED;
    }

    return res;
}

static int32_t _core_tcp_recv_packet(core_tcp_handle_t *tcp_handle, uint32_t *body_total_len)
{
    int32_t res = STATE_SUCCESS;
    char *line = NULL;
    uint32_t idx = 0, line_max_len = tcp_handle->header_line_max_len;
    uint64_t timenow_ms = 0;

    line = tcp_handle->sysdep->core_sysdep_malloc(line_max_len, CORE_TCP_MODULE_NAME);
    if (line == NULL) {
        return STATE_SYS_DEPEND_MALLOC_FAILED;
    }
    memset(line, 0, line_max_len);

    timenow_ms = tcp_handle->sysdep->core_sysdep_time();
    for (idx = 0; idx < line_max_len;) {
        if (timenow_ms > tcp_handle->sysdep->core_sysdep_time()) {
            timenow_ms = tcp_handle->sysdep->core_sysdep_time();
        }
        if (tcp_handle->sysdep->core_sysdep_time() - timenow_ms >= tcp_handle->recv_timeout_ms) {
            res =  STATE_TCP_HEADER_INVALID;
            break;
        }
        if (idx + 2 > line_max_len) {
            res = STATE_TCP_HEADER_BUFFER_TOO_SHORT;
            break;
        }

        // 接收单字节超时200ms 则表示一包数据完成；
        if ((res = _core_tcp_recv(tcp_handle, (uint8_t *)&line[idx], 1, 200)) < STATE_SUCCESS) {
            break;
        }
        if (res == 0){ //读取到0字节数据 超时时间内；
            break;
        }
        idx++;
    }

    // 单包数据处理
    if (idx > 1 && res >= 0) 
    {
        *body_total_len = idx;
        aiot_tcp_recv_t packet;

        if (tcp_handle->core_recv_handler != NULL) {
            tcp_handle->session.body_read_len = idx;
            memset(&packet, 0, sizeof(aiot_tcp_recv_t));
            packet.type = AIOT_TCPRECV_BODY;
            packet.data.body.buffer = (uint8_t *)line;
            packet.data.body.len = idx;

            core_log_hexdump(STATE_TCP_LOG_RECV_CONTENT, '<', (uint8_t *)line, idx);
            tcp_handle->core_recv_handler(tcp_handle, &packet, tcp_handle->core_userdata);
        }
        idx = 0;
    } else {
        res = STATE_TCP_READ_BODY_FINISHED;
        *body_total_len= 0;
    }

    tcp_handle->sysdep->core_sysdep_free(line);

    return res;
}

static int32_t _core_tcp_recv_auth(core_tcp_handle_t *tcp_handle, uint32_t *body_total_len)
{
    int32_t res = STATE_SUCCESS;
    char *line = NULL;
    uint32_t idx = 0, line_max_len = tcp_handle->header_line_max_len;
    uint64_t timenow_ms = 0;

    line = tcp_handle->sysdep->core_sysdep_malloc(line_max_len, CORE_TCP_MODULE_NAME);
    if (line == NULL) {
        return STATE_SYS_DEPEND_MALLOC_FAILED;
    }
    memset(line, 0, line_max_len);

    timenow_ms = tcp_handle->sysdep->core_sysdep_time();
    for (idx = 0; idx < line_max_len;) {
        if (timenow_ms > tcp_handle->sysdep->core_sysdep_time()) {
            timenow_ms = tcp_handle->sysdep->core_sysdep_time();
        }
        if (tcp_handle->sysdep->core_sysdep_time() - timenow_ms >= tcp_handle->recv_timeout_ms) {
            res =  STATE_TCP_HEADER_INVALID;
            break;
        }
        if (idx + 2 > line_max_len) {
            res = STATE_TCP_HEADER_BUFFER_TOO_SHORT;
            break;
        }

        // 接收单字节超时200ms 则表示一包数据完成；
        if ((res = _core_tcp_recv(tcp_handle, (uint8_t *)&line[idx], 1, 200)) < STATE_SUCCESS) {
            break;
        }
        if (idx > 0 && res == 0){ //读取到0字节数据 超时时间内；
            break;
        }
        idx++;
    }

    // core_log3(tcp_handle->sysdep, STATE_TCP_LOG_RECV_HEADER, "< %d %x %d\r\n", &idx, &res, &tcp_handle->recv_timeout_ms);
    
    // 单包数据处理
    if (idx > 1 && (res >= 0)) 
    {
        *body_total_len = idx;
        aiot_tcp_recv_t packet;

        if (tcp_handle->core_recv_handler != NULL) {
            tcp_handle->session.body_read_len = idx;
            memset(&packet, 0, sizeof(aiot_tcp_recv_t));
            packet.type = AIOT_TCPRECV_AUTH;
            packet.data.body.buffer = (uint8_t *)line;
            packet.data.body.len = idx;

            // core_log_hexdump(STATE_TCP_LOG_RECV_CONTENT, '<', (uint8_t *)line, idx);
            tcp_handle->core_recv_handler(tcp_handle, &packet, tcp_handle->core_userdata);
        }
        idx = 0;
        res = STATE_SUCCESS;
    }

    tcp_handle->sysdep->core_sysdep_free(line);
    return res;
}

int32_t core_tcp_recv(void *handle)
{
    int32_t res = STATE_SUCCESS;
    uint32_t body_total_len = 0;
    core_tcp_handle_t *tcp_handle = (core_tcp_handle_t *)handle;

    if (tcp_handle == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    if (tcp_handle->network_handle == NULL) {
        return STATE_SYS_DEPEND_NWK_CLOSED;
    }

    if (tcp_handle->core_exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    _core_tcp_exec_inc(tcp_handle);

    tcp_handle->sysdep->core_sysdep_mutex_lock(tcp_handle->recv_mutex);
    res = _core_tcp_recv_packet(tcp_handle, &body_total_len);
    tcp_handle->sysdep->core_sysdep_mutex_unlock(tcp_handle->recv_mutex);
    if (res < STATE_SUCCESS) {
        _core_tcp_exec_dec(tcp_handle);
        return res;
    }

    _core_tcp_exec_dec(tcp_handle);

    return res;
}

int32_t core_tcp_recv_auth(void *handle)
{
    int32_t res = STATE_SUCCESS;
    uint32_t body_total_len = 0;
    core_tcp_handle_t *tcp_handle = (core_tcp_handle_t *)handle;

    if (tcp_handle == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    if (tcp_handle->network_handle == NULL) {
        return STATE_SYS_DEPEND_NWK_CLOSED;
    }

    if (tcp_handle->core_exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    _core_tcp_exec_inc(tcp_handle);

    tcp_handle->sysdep->core_sysdep_mutex_lock(tcp_handle->recv_mutex);
    res = _core_tcp_recv_auth(tcp_handle, &body_total_len);
    tcp_handle->sysdep->core_sysdep_mutex_unlock(tcp_handle->recv_mutex);
    if (res < STATE_SUCCESS) {
        _core_tcp_exec_dec(tcp_handle);
        return res;
    }

    _core_tcp_exec_dec(tcp_handle);

    return res;
}

int32_t core_tcp_deinit(void **p_handle)
{
    uint32_t deinit_timeout_ms = 0;
    core_tcp_handle_t *tcp_handle = NULL;

    if (p_handle == NULL || *p_handle == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    tcp_handle = *(core_tcp_handle_t **)p_handle;

    if (tcp_handle->core_exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    tcp_handle->exec_enabled = 0;
    deinit_timeout_ms = tcp_handle->deinit_timeout_ms;
    do {
        if (tcp_handle->exec_count == 0) {
            break;
        }
        tcp_handle->sysdep->core_sysdep_sleep(CORE_TCP_DEINIT_INTERVAL_MS);
    } while ((deinit_timeout_ms > CORE_TCP_DEINIT_INTERVAL_MS) && (deinit_timeout_ms - CORE_TCP_DEINIT_INTERVAL_MS > 0));

    if (tcp_handle->exec_count != 0) {
        return STATE_TCP_DEINIT_TIMEOUT;
    }

    if (tcp_handle->network_handle != NULL) {
        tcp_handle->sysdep->core_sysdep_network_deinit(&tcp_handle->network_handle);
    }

    if (tcp_handle->host != NULL) {
        tcp_handle->sysdep->core_sysdep_free(tcp_handle->host);
    }
    if (tcp_handle->cred != NULL) {
        tcp_handle->sysdep->core_sysdep_free(tcp_handle->cred);
    }

    tcp_handle->sysdep->core_sysdep_mutex_deinit(&tcp_handle->data_mutex);
    tcp_handle->sysdep->core_sysdep_mutex_deinit(&tcp_handle->send_mutex);
    tcp_handle->sysdep->core_sysdep_mutex_deinit(&tcp_handle->recv_mutex);

    tcp_handle->sysdep->core_sysdep_free(tcp_handle);

    *p_handle = NULL;

    return STATE_SUCCESS;
}

